﻿using System;
using System.Collections;
using System.ComponentModel;
using System.Web.UI;


namespace BizElements.BusinessLayer
{
    /// <summary>
    /// Value-name data source wrapper for <b>IList</b> and <b>IListSource</b> objects.
    /// </summary>
    [Serializable]
    public sealed class CodeList : ICodeList
    {
        #region Fields.

        string valueMember;
        string displayMember;
        IList list;             // Eg. Arraylist.
        IListSource listSource; // Eg. DataTable.
        Hashtable index;

        #endregion

        #region Constructors.

        /// <summary>
        /// Initializes a new instance of <b>CodeList</b> class.
        /// </summary>
        /// <param name="dataSource">Data source.</param>
        /// <param name="valueMember">Data source field which provides a key value.</param>
        /// <param name="displayMember">Data source field which provides a display value.</param>
        public CodeList(IList dataSource, string valueMember, string displayMember)
        {
            this.list = dataSource;
            this.valueMember = valueMember;
            this.displayMember = displayMember;
        }

        /// <summary>
        /// Initializes a new instance of <b>CodeList</b> class.
        /// </summary>
        /// <param name="dataSource">Data source.</param>
        /// <param name="valueMember">Data source field which provides a key value.</param>
        /// <param name="displayMember">Data source field which provides a display value.</param>
        public CodeList(IListSource dataSource, string valueMember, string displayMember)
        {
            this.listSource = dataSource;
            this.valueMember = valueMember;
            this.displayMember = displayMember;
        }

        #endregion

        #region CreateIndex.

        /// <summary>
        /// Creates data source index which significantly enhances the performance of <see cref="GetDisplayValue"/> method.
        /// </summary>
        /// <remarks>It is recommended to create index if the <b>GetDisplayValue</b> method will be used multiple times.</remarks>
        public void CreateIndex()
        {
            if (this.DataSource.Count == 0 || this.index != null)
                return;

            this.index = new Hashtable(this.DataSource.Count);
            foreach (object item in this.DataSource)
            {
                string keyValue = DataBinder.Eval(item, this.valueMember, null);
                object displayValue = DataBinder.Eval(item, this.displayMember);
                this.index.Add(keyValue, displayValue);
            }
        }

        #endregion

        #region GetDisplayValue.

        /// <summary>
        /// Gets the display value associated with the specified key value.
        /// </summary>
        /// <param name="key">Value stored in the data source's key field.</param>
        /// <returns>Display value or <b>null</b> if the given key field is not found in the code list.</returns>
        /// <remarks>Consider creating index with <see cref="CreateIndex"/> method if the <b>GetDisplayValue</b>
        /// is frequently used.</remarks>
        public object GetDisplayValue(object key)
        {
            if (this.DataSource.Count == 0)
                return null;

            if (this.index != null)
            {
                // Use created index.
                return this.index[Convert.ToString(key)];
            }
            else
            {
                // Sequential scan.	
                string sKey = Convert.ToString(key);
                foreach (object item in this.DataSource)
                {
                    string currentKeyValue = DataBinder.Eval(item, this.valueMember, null);
                    if (currentKeyValue == sKey)
                        return DataBinder.Eval(item, this.displayMember);
                }

                return null;
            }
        }

        #endregion

        #region Properties.

        /// <summary>
        /// Gets data source field which provides a key value.
        /// </summary>
        public string ValueMember
        {
            get { return this.valueMember; }
        }

        /// <summary>
        /// Gets data source field which provides a display value.
        /// </summary>
        public string DisplayMember
        {
            get { return this.displayMember; }
        }

        [NonSerialized]
        IList cachedNonserializableList;

        /// <summary>
        /// Gets data source wrapped by the current <b>CodeList</b>.
        /// </summary>
        public IList DataSource
        {
            get
            {
                if (this.listSource != null)
                {
                    // Eg. DataTable.GetList() returns DataView which is not serializable.
                    if (this.cachedNonserializableList == null)
                        this.cachedNonserializableList = this.listSource.GetList();

                    return this.cachedNonserializableList;
                }
                else
                {
                    return this.list;
                }
            }
        }

        #endregion

        #region IListSource.

        /// <summary>
        /// Returns <b>false</b>.
        /// </summary>
        public bool ContainsListCollection
        {
            get { return false; }
        }

        /// <summary>
        /// Gets <see cref="DataSource"/>.
        /// </summary>
        /// <returns>Data source wrapped by the current <b>CodeList</b>.</returns>
        public IList GetList()
        {
            return this.DataSource;
        }

        #endregion
    }
}